import time
import json
import threading
from pynput import mouse, keyboard
import tkinter as tk
from tkinter import ttk, messagebox

# 定义一个RecorderApp类，用于录制和回放鼠标和键盘操作
class RecorderApp:
    def __init__(self):
        # 初始化操作录制列表、录制状态、回放停止标志、鼠标监听器和键盘监听器
        self.recording = []  # 存储录制的操作序列
        self.is_recording = False  # 录制状态标志
        self.stop_replay_flag = False  # 回放停止标志
        self.mouse_listener = None  # 鼠标监听器
        self.keyboard_listener = None  # 键盘监听器

        # 创建主窗口并设置标题和大小
        self.root = tk.Tk()
        self.root.title("操作录制器 v1.0")
        self.root.geometry("400x250")

        # 创建控制面板框架，并将其放置在窗口中
        control_frame = ttk.LabelFrame(self.root, text="控制面板")
        control_frame.pack(pady=10, padx=20, fill="x")

        # 绑定快捷键F5开始录制，F6停止录制
        self.root.bind("<F5>", lambda e: self.start_recording())  # F5开始录制
        self.root.bind("<F6>", lambda e: self.stop_recording())  # F6停止录制

        # 在控制面板中添加开始录制、停止录制、保存操作、开始回放、停止回放、加载操作按钮
        ttk.Button(control_frame, text="开始录制", command=self.start_recording).grid(row=0, column=0, padx=5, pady=5)
        ttk.Button(control_frame, text="停止录制", command=self.stop_recording).grid(row=0, column=1, padx=5, pady=5)
        ttk.Button(control_frame, text="保存操作", command=self.save_recording).grid(row=0, column=2, padx=5, pady=5)
        ttk.Button(control_frame, text="开始回放", command=self.start_replay).grid(row=1, column=0, padx=5, pady=5)
        ttk.Button(control_frame, text="停止回放", command=self.stop_replay).grid(row=1, column=1, padx=5, pady=5)
        ttk.Button(control_frame, text="加载操作", command=self.load_recording).grid(row=1, column=2, padx=5, pady=5)

        # 创建状态显示标签，初始状态为“就绪”
        self.status_var = tk.StringVar(value="就绪")
        status_bar = ttk.Label(self.root, textvariable=self.status_var, relief="sunken")
        status_bar.pack(side="bottom", fill="x")

        # 创建操作列表框架，并将其放置在窗口中
        list_frame = ttk.LabelFrame(self.root, text="录制事件")
        list_frame.pack(pady=10, padx=20, fill="both", expand=True)

        # 创建列表框用于显示录制的操作事件
        self.listbox = tk.Listbox(list_frame, height=8)
        self.listbox.pack(fill="both", expand=True, padx=5, pady=5)

        # 设置程序关闭时的清理操作
        self.root.protocol("WM_DELETE_WINDOW", self.on_close)
        # 进入Tkinter的主事件循环
        self.root.mainloop()

    # === 核心功能 ===
    def start_recording(self):
        """开始录制操作序列"""
        # 清空之前的录制数据，设置为正在录制状态
        self.recording = []
        self.is_recording = True
        self.status_var.set("录制中... (按ESC停止)")

        # 启动鼠标监听器，监听点击和移动事件
        self.mouse_listener = mouse.Listener(
            on_click=self.on_click,
            on_move=self.on_move
        )
        self.mouse_listener.start()

        # 启动键盘监听器，监听按键按压和释放事件
        self.keyboard_listener = keyboard.Listener(
            on_press=self.on_press,
            on_release=self.on_release
        )
        self.keyboard_listener.start()

    def stop_recording(self):
        """停止录制"""
        # 如果正在录制，则停止录制并更新状态显示
        if self.is_recording:
            self.is_recording = False
            if self.mouse_listener:
                self.mouse_listener.stop()
            if self.keyboard_listener:
                self.keyboard_listener.stop()
            self.status_var.set(f"已停止录制 - {len(self.recording)}个事件")

    def save_recording(self):
        """保存操作序列到文件"""
        # 如果没有录制数据，则显示警告信息
        if not self.recording:
            messagebox.showwarning("警告", "没有可保存的操作序列")
            return

        # 将录制的操作序列保存到JSON文件中
        with open("operation_sequence.json", "w") as f:
            json.dump(self.recording, f)
        self.status_var.set("操作序列已保存到 operation_sequence.json")

    def load_recording(self):
        """从文件加载操作序列"""
        try:
            # 从JSON文件中读取操作序列并加载到录制列表中
            with open("operation_sequence.json", "r") as f:
                self.recording = json.load(f)
            self.update_listbox()
            self.status_var.set(f"已加载 {len(self.recording)} 个操作事件")
        except FileNotFoundError:
            # 如果文件不存在，则显示错误信息
            messagebox.showerror("错误", "找不到保存的操作序列文件")

    def start_replay(self):
        """开始回放操作"""
        # 如果没有录制数据，则显示警告信息
        if not self.recording:
            messagebox.showwarning("警告", "没有可回放的操作序列")
            return

        # 设置回放停止标志为False，并启动一个线程进行操作回放
        self.stop_replay_flag = False
        threading.Thread(target=self.replay_operations, daemon=True).start()
        self.status_var.set("回放中...")

    def stop_replay(self):
        """停止回放"""
        # 设置回放停止标志为True，并更新状态显示
        self.stop_replay_flag = True
        self.status_var.set("回放已停止")

    def on_close(self):
        """关闭程序时的清理"""
        # 确认是否退出程序，并停止录制和回放
        if messagebox.askyesno("退出", "确定退出吗？"):
            self.stop_recording()
            self.stop_replay()
            self.root.destroy()

    # === 事件处理 ===
    def on_click(self, x, y, button, pressed):
        """处理鼠标点击事件"""
        # 如果不在录制状态，则终止事件处理
        if not self.is_recording:
            return False

        # 根据鼠标按钮的按下或释放状态设置事件类型
        event_type = "按下" if pressed else "释放"
        # 创建鼠标点击事件字典
        event = {
            "type": "mouse",
            "action": f"鼠标{button.name}{event_type}",
            "x": x,
            "y": y,
            "button": button.name,
            "pressed": pressed,
            "time": time.time()
        }
        self.record_event(event)

    def on_move(self, x, y):
        """处理鼠标移动事件 (可选录制)"""
        # 如果不在录制状态，则终止事件处理
        if not self.is_recording:
            return

        # 为减少数据量，只记录重要移动（间隔大于50像素）
        if self.recording and self.recording[-1]["type"] == "mouse":
            last_event = self.recording[-1]
            if (abs(x - last_event["x"]) > 50 or
                    abs(y - last_event["y"]) > 50):
                # 创建鼠标移动事件字典
                event = {
                    "type": "mouse",
                    "action": "鼠标移动",
                    "x": x,
                    "y": y,
                    "time": time.time()
                }
                self.record_event(event)

    def on_press(self, key):
        """处理键盘按下事件"""
        # 如果不在录制状态，则终止事件处理
        if not self.is_recording:
            return False

        # 尝试获取按键的字符表示，如果按键没有字符表示（如功能键），则使用按键名称
        try:
            key_str = key.char
        except AttributeError:
            key_str = key.name

        # 如果按下的是ESC键，则停止录制
        if key == keyboard.Key.esc:
            self.stop_recording()
            return False

        # 创建键盘按下事件字典
        event = {
            "type": "keyboard",
            "action": "按键按下",
            "key": key_str,
            "time": time.time()
        }
        self.record_event(event)

    def on_release(self, key):
        """处理键盘释放事件"""
        # 如果不在录制状态，则终止事件处理
        if not self.is_recording:
            return

        # 尝试获取按键的字符表示，如果按键没有字符表示（如功能键），则使用按键名称
        try:
            key_str = key.char
        except AttributeError:
            key_str = key.name

        # 创建键盘释放事件字典
        event = {
            "type": "keyboard",
            "action": "按键释放",
            "key": key_str,
            "time": time.time()
        }
        self.record_event(event)

    # === 辅助方法 ===
    def record_event(self, event):
        """记录事件并更新界面"""
        # 将事件添加到录制列表中，并更新列表框显示
        self.recording.append(event)
        self.update_listbox()

    def update_listbox(self):
        """更新事件列表显示"""
        # 清空列表框内容
        self.listbox.delete(0, tk.END)
        # 只显示最后20个事件
        for event in self.recording[-20:]:
            if event["type"] == "mouse":
                if "button" in event:
                    action = f"{event['action']} ({event['x']}, {event['y']})"
                else:
                    action = f"鼠标移动到 ({event['x']}, {event['y']})"
            else:
                action = f"{event['action']}: {event['key']}"
            # 将事件添加到列表框中
            self.listbox.insert(tk.END, action)

    def replay_operations(self):
        """回放操作序列"""
        # 记录开始回放的时间
        start_time = time.time()
        # 创建鼠标和键盘控制器用于模拟操作
        controller_mouse = mouse.Controller()
        controller_keyboard = keyboard.Controller()

        # 遍历录制的操作序列，根据事件类型模拟鼠标或键盘操作
        for event in self.recording:
            if self.stop_replay_flag:
                break

            # 计算等待时间以匹配录制时的操作间隔
            elapsed = time.time() - start_time
            wait_time = event["time"] - self.recording[0]["time"] - elapsed
            if wait_time > 0:
                time.sleep(wait_time)

            # 根据事件类型执行相应的操作
            if event["type"] == "mouse":
                # 设置鼠标位置
                controller_mouse.position = (event["x"], event["y"])
                if "button" in event:
                    # 模拟鼠标按钮按下或释放
                    button = getattr(mouse.Button, event["button"])
                    if event["pressed"]:
                        controller_mouse.press(button)
                    else:
                        controller_mouse.release(button)

            elif event["type"] == "keyboard":
                try:
                    # 尝试将按键名称转换为键盘按键对象
                    key = keyboard.Key[event["key"]]
                except KeyError:
                    # 如果按键名称未找到，则使用按键名称字符串
                    key = event["key"]

                # 根据事件操作类型模拟按键按下或释放
                if event["action"] == "按键按下":
                    controller_keyboard.press(key)
                else:
                    controller_keyboard.release(key)

        # 更新状态显示为回放完成
        self.status_var.set("回放完成")
        self.root.after(0, lambda: self.status_var.set("回放完成"))


if __name__ == "__main__":
    # 创建RecorderApp类实例并启动程序
    RecorderApp()
